/**
 * 
 */
package com.novartis.nims.trialsubmitter.dicom;

import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;

import org.rsna.ctp.stdstages.anonymizer.dicom.FnCall;

/**
 * @author Josh Snyder
 *
 */
public class RuleParser {
	
	private Properties _rules;
	
	static final String REMOVE_FUNCTION = "remove";
	static final String EMPTY_FUNCTION = "empty";
	static final String KEEP_FUNCTION = "keep";

	/**
	 * 
	 */
	public RuleParser(Properties rules) {
		_rules = rules;
	}
	
	public Properties selectRulesExcluding(Properties rules) {
		Properties selectedRules = new Properties();
		
		for (Enumeration<Object> it = _rules.keys(); it.hasMoreElements(); ) {
			String key = (String) it.nextElement();
			if (! rules.containsKey(key)) {
				selectedRules.setProperty(key, _rules.getProperty(key));
			}
		}
		
		return selectedRules;
	}
	
	/**
	 * Select all of the rules which can only maintain, remove, or scramble existing content.
	 * A client may want to use these as a ruleset to be applied recursively through nested
	 * DICOM datasets.
	 * 
	 * @return The collection of rules selected
	 */
	public Properties selectContentRemovalRules() {
		Properties selectedRules = new Properties();
    	
    	for (Enumeration<Object> it = _rules.keys(); it.hasMoreElements(); ) {
			String key = (String) it.nextElement();
			String command = _rules.getProperty(key);
			
			if (ruleIsGlobalOrParam(key) || functionIsInListOrIfFunction(extractRuleFunction(command), Arrays.asList(KEEP_FUNCTION, REMOVE_FUNCTION, DicomCleaner.hashuidFn))) {
				selectedRules.setProperty(key, command);
			}
		}
    	
    	return selectedRules;
	}
	
	private boolean functionIsInListOrIfFunction(FnCall function, List<String> functionList) {
		if (function.length == -1) {
			return false;
		} else {
			if (function.name.equals(DicomCleaner.ifFn)) {
				return functionIsInListOrIfFunction(extractRuleFunction(function.trueCode), Arrays.asList(KEEP_FUNCTION)) &&
					   functionIsInListOrIfFunction(extractRuleFunction(function.falseCode), Arrays.asList(EMPTY_FUNCTION, DicomCleaner.hashuidFn));
			} else if (functionList.contains(function.name)) {
				return true;
			} else {
				return false;
			}
		}
	}
	
	public Properties selectNestedElementRules() {
		Properties selectedRules = new Properties();
    	
    	for (Enumeration<Object> it = _rules.keys(); it.hasMoreElements(); ) {
			String key = (String) it.nextElement();
			if (key.startsWith("set.") && ruleIsForNestedElement(key)) {
				selectedRules.setProperty(key, _rules.getProperty(key));
			}
		}
    	
    	return selectedRules;
	}
	
	private boolean ruleIsGlobalOrParam(String ruleKey) {
		if (ruleKey.startsWith("param.") ||
			ruleKey.startsWith("remove.")) {
			return true;
		} else {
			return false;
		}
	}
	
	private boolean ruleIsForNestedElement(String ruleKey) {
		String tagString = getTagString(ruleKey);
		
		if (tagString.length() > 11) {
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * 
	 * 
	 * @param rule
	 * @return
	 */
	static FnCall extractRuleFunction(String rule) {
		FnCall fnCall = new FnCall("@default()", null, null, null, 0);
		
		//TODO: respect escape characters
		int functionIndex = rule.indexOf(DicomCleaner.functionChar);
		if (functionIndex != -1) {
			//FnCall does not make use of the last 4 constructor params, so we pass it nulls and arbitrary defaults
			fnCall = new FnCall(rule.substring(functionIndex + 1), null, null, null, 0);
		} else {
			fnCall.length = -1;
		}
		
		return fnCall;
	}
	
	//Find "[gggg,eeee]" in a String and
	//return a tagString in the form "(gggg,eeee)"
	static final String DEFAULT_TAG_STRING = "(0000,0000)";
	static String getTagString(String key) {
		int k = key.indexOf("[");
		if (k < 0) return DEFAULT_TAG_STRING;
		int kk = key.indexOf("]",k);
		if (kk < 0) return DEFAULT_TAG_STRING;
		return ("(" + key.substring(k+1,kk) + ")").toLowerCase();
	}

}
